#include <QPainter>
#include <QStyledItemDelegate>
#include <QProcess>
#include <QTextStream>
#include <QFileDialog>
#include <QDebug>

#include "chessgrid.h"
#include "board.h"
#include "setupdialog.h"
#include "gameoverdlg.h"
#include "aboutdlg.h"
#include "helpdlg.h"

#include "mainwindow.h"
#include "ui_mainwindow.h"



MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    connect(ui->action_setup, SIGNAL(triggered(bool)), this, SLOT(openSetupDlg()));
    connect(ui->randomDiceButton, SIGNAL(pressed()), this, SLOT(randomDice()));
    connect(ui->chooseDiceButton, SIGNAL(pressed()), this, SLOT(chooseDice()));
    connect(ui->action_how_to_use, SIGNAL(triggered(bool)), this, SLOT(openHelpDlg()));
    connect(ui->actionabout, SIGNAL(triggered(bool)), this, SLOT(openAboutDlg()));
    connect(ui->action_clear, SIGNAL(triggered(bool)), this, SLOT(clearOutput()));
    connect(ui->loadEngineR, SIGNAL(pressed()), this, SLOT(loadEngineRClicked()));
    connect(ui->loadEngineB, SIGNAL(pressed()), this, SLOT(loadEngineBClicked()));
    connect(ui->unloadEngineR, SIGNAL(pressed()), this, SLOT(unloadEngineRClicked()));
    connect(ui->unloadEngineB, SIGNAL(pressed()), this, SLOT(unloadEngineBClicked()));
    connect(ui->action_new_game, SIGNAL(triggered(bool)), this, SLOT(newGame()));
    connect(ui->action_reset, SIGNAL(triggered(bool)), this, SLOT(reset()));
    for(int i = 0; i < 5; i++)
        for(int j = 0; j < 5; j++)
            connect(ui->board->getGrid(i, j), SIGNAL(pressed(int,int,int,int)), this, SLOT(receiveGridPressed(int,int,int,int)));

    QStyledItemDelegate * delegate = new QStyledItemDelegate();
    ui->comboBox->setItemDelegate(delegate);

    this->setDiceEnabled(false);
    //this->setEngineBlueEnabled(false);
    //this->setEngineRedEnabled(false);
    receiveSettings(1, 123456, 123456);
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::openSetupDlg()
{
    SetupDialog * dlg = new SetupDialog(this);
    connect(dlg, SIGNAL(emitSettings(int,int,int)), this, SLOT(receiveSettings(int,int,int)));
    dlg->show();
}

void MainWindow::receiveSettings(int first, int rlayout, int blayout)
{
    memset(chess, 0, sizeof(chess));

    this->first = first;

    char buffer[10];
    sprintf(buffer, "%d", rlayout);
    ui->layoutR->setText(buffer);
    sprintf(buffer, "%d", blayout);
    ui->layoutB->setText(buffer);

    chess[3][1] = (rlayout % 10) * 10 + 1;
    rlayout /= 10;
    chess[2][2] = (rlayout % 10) * 10 + 1;
    rlayout /= 10;
    chess[2][1] = (rlayout % 10) * 10 + 1;
    rlayout /= 10;
    chess[1][3] = (rlayout % 10) * 10 + 1;
    rlayout /= 10;
    chess[1][2] = (rlayout % 10) * 10 + 1;
    rlayout /= 10;
    chess[1][1] = (rlayout % 10) * 10 + 1;

    chess[3][5] = (blayout % 10) * 10 + 2;
    blayout /= 10;
    chess[4][4] = (blayout % 10) * 10 + 2;
    blayout /= 10;
    chess[4][5] = (blayout % 10) * 10 + 2;
    blayout /= 10;
    chess[5][3] = (blayout % 10) * 10 + 2;
    blayout /= 10;
    chess[5][4] = (blayout % 10) * 10 + 2;
    blayout /= 10;
    chess[5][5] = (blayout % 10) * 10 + 2;

    ui->board->updateGrid(chess);
}

void MainWindow::randomDice()
{
    srand(time(0));
    dice = rand() % 6 + 1;
    char buffer[5];
    sprintf(buffer, "%d", dice);
    ui->lcdNumber->display(buffer);

    receiveDice(dice);
}

void MainWindow::chooseDice()
{
    dice = ui->comboBox->currentText().toInt();
    char buffer[5];
    sprintf(buffer, "%d", dice);
    ui->lcdNumber->display(buffer);

    receiveDice(dice);
}

void MainWindow::setDiceEnabled(bool flag)
{
    ui->randomDiceButton->setEnabled(flag);
    ui->chooseDiceButton->setEnabled(flag);
    ui->comboBox->setEnabled(flag);
    ui->lcdNumber->setEnabled(flag);
}

void MainWindow::receiveDice(int dice)
{
    int ad1, ad2;

    getAdjacent(chess, dice * 10 + move, move, ad1, ad2);
    if(move == 1)
    {
        out(QString("<font color = \"#ff534d\">red:</font>"));
        out(QString("<font color = \"#ff534d\">dice at %1</font>").arg(dice));
    }
    else if(move == 2)
    {
        out(QString("<font color = \"#25c6fc\">blue:</font>"));
        out(QString("<font color = \"#25c6fc\">dice at %1</font>").arg(dice));
    }
    //out(QString("you can move %1 %2 %3\n").arg(dice).arg(ad1 / 10).arg(ad2 / 10));

    if(!isAI[move])
    {
        for(int i = 1; i <= 5; i++)
        {
            for(int j = 1; j <= 5; j++)
            {
                if(chess[i][j] == ad1 || chess[i][j] == ad2 || chess[i][j] == dice * 10 + move)
                {
                    ui->board->setGridState(i - 1, j - 1, 1);
                }
            }
        }
    }
    else
    {
        if(move == 1)
        {
            //out(lastMove);
            //engine1->write(lastMove);
            engine1->write(QString("%1\r\n").arg(dice).toStdString().c_str());
            //engine1->waitForReadyRead();
        }
        else if(move == 2)
        {
            //out(lastMove);
            //engine2->write(lastMove);
            engine2->write(QString("%1\r\n").arg(dice).toStdString().c_str());
        }
        for(int i = 1; i <= 5; i++)
        {
            for(int j = 1; j <= 5; j++)
            {
                if(chess[i][j] == ad1 || chess[i][j] == ad2 || chess[i][j] == dice * 10 + move)
                {
                    ui->board->setGridState(i - 1, j - 1, 3);
                }
            }
        }
    }

    ui->board->update();
}

void MainWindow::getAdjacent(int board[6][6], int pawn, int player, int & ad1, int & ad2)
{
    ad1 = 0x3f3f3f3f;
    ad2 = -0x3f3f3f3f;

    //if(player == 1)
    //{
        for(int i = 1; i <= 5; i++)
        {
            for(int j = 1; j <= 5; j++)
            {
                if(chess[i][j] != 0 && chess[i][j] % 10 == player && chess[i][j] >= pawn && chess[i][j] - pawn < ad1 - pawn)
                    ad1 = chess[i][j];
                if(chess[i][j] != 0 && chess[i][j] % 10 == player && chess[i][j] <= pawn && pawn - chess[i][j] < pawn - ad2)
                    ad2 = chess[i][j];
            }
        }
    //}
}

void MainWindow::receiveGridPressed(int player, int i, int j, int what)
{
    static int ox, oy;

    //out(QString("pressed %1 %2\n").arg(i).arg(j));
    if(what == 1)
    {
        ox = i;
        oy = j;
        ui->board->clearState(4);
        //if(player == 2)
        //{
            for(int ii = 4 * player - 3; ii <= 4 * player - 1; ii++)
            {
                int x = i + u[ii];
                int y = j + v[ii];
                if(x >= 1 && x <= 5 && y >= 1 && y <= 5)
                {
                    ui->board->setGridState(x - 1, y - 1, 4);
                }
            }
        //}
    }
    else if(what == 2)
    {
        // move from (ox, oy) to (i, j)
        if(move == 1)
            out(QString("<font color = \"#ff534d\">move %1%2 to %3%4<br></font>").arg(ox - 1).arg(oy - 1).arg(i - 1).arg(j - 1));
        else if(move == 2)
            out(QString("<font color = \"#25c6fc\">move %1%2 to %3%4<br></font>").arg(ox - 1).arg(oy - 1).arg(i - 1).arg(j - 1));
        sprintf(lastMove, "move %d %d %d\r\n\0", (ox - 1) * 10 + oy - 1, (i - 1) * 10 + j - 1, 0);

        makeMove(ox, oy, i, j);
        ui->board->updateGrid(chess);
        ui->board->clearState(5);
        ui->board->clearState(4);
        ui->board->clearState(1);

        move = 3 - move;
        if(isAI[move])
        {
            if(move == 1)
                sendToEngine(lastMove, engine1);
            else if(move == 2)
                sendToEngine(lastMove, engine2);
        }

        int t = isGameOver();
        if(t)
            doGameOver(t);
    }
    else if(what == 0)
    {
        ui->board->clearState(5);
        ui->board->clearState(4);
    }

    ui->board->update();
}

void MainWindow::out(const QString & str)
{
    ui->textEdit->append(str);
}

void MainWindow::makeMove(int ox, int oy, int tx, int ty)
{
    chess[tx][ty] = chess[ox][oy];
    chess[ox][oy] = 0;
}

QProcess * MainWindow::openEngine(QString & filename)
{
    QProcess * pro = new QProcess(this);

    //connect(engine1, SIGNAL(readyReadStandardOutput()), this, SLOT(receiveEngineDataR()));

    //engine1->setProcessChannelMode(QProcess::MergedChannels);


    return pro;
}

void MainWindow::sendToEngine(const QString & cmd, QProcess * engine)
{
    //engine1->write(str.toLatin1().constData(), str.length());
    engine->write(cmd.toLatin1().constData(), cmd.length());
}

void MainWindow::receiveEngineDataR()
{
    static int flag = 0;
    QString data = engine1->readAll();
    //out(QString("%1\n").arg(flag));
    //out(data);

    if(flag == 0)
    {
        ui->redName->setText(data);
        flag = 1;
    }
    else if(flag == 1)
    {
        flag = 2;
    }
    else if(flag == 2)
    {
        // move
        char buf[10];
        int from, to;
        double rate;
        sscanf(data.toStdString().c_str(), "%lf%s%d%d", &rate, buf, &from, &to);
        makeMove(from / 10 + 1, from % 10 + 1, to / 10 + 1, to % 10 + 1);
        out(QString("<font color = \"#ff534d\">move %1 to %2<br>rate:%3<br></font>").arg(from).arg(to).arg(rate));

        move = 3 - move;
        ui->board->updateGrid(chess);
        ui->board->clearState(3);

        int t = isGameOver();
        if(t)
            doGameOver(t);
    }
}

void MainWindow::receiveEngineDataB()
{
    static int flag = 0;
    QString data = engine2->readAll();
    //out(QString("%1\n").arg(flag));
    //out(data);

    if(flag == 0)
    {
        ui->blueName->setText(data);
        flag = 1;
    }
    else if(flag == 1)
    {
        flag = 2;
    }
    else if(flag == 2)
    {
        // move
        char buf[10];
        int from, to;
        double rate;
        sscanf(data.toStdString().c_str(), "%lf%s%d%d", &rate, buf, &from, &to);
        out(QString("<font color = \"#25c6fc\">move %1 to %2<br>rate:%3<br><font>").arg(from).arg(to).arg(rate));
        makeMove(from / 10 + 1, from % 10 + 1, to / 10 + 1, to % 10 + 1);

        move = 3 - move;
        ui->board->updateGrid(chess);
        ui->board->clearState(3);

        int t = isGameOver();
        if(t)
            doGameOver(t);
    }
}

void MainWindow::loadEngineRClicked()
{
    QString filename = QFileDialog::getOpenFileName(this, "open engine");
    engine1 = openEngine(filename);

    connect(engine1, SIGNAL(readyReadStandardOutput()), this, SLOT(receiveEngineDataR()));

    engine1->start(filename);

    engine1->waitForStarted();

    sendToEngine("name?\r\n", engine1);
    engine1->waitForReadyRead();
    if(first == 1)
        sendToEngine("redfirst\r\n", engine1);
    else if(first == 2)
        sendToEngine("bluefirst\r\n", engine1);
    sendToEngine("red\r\n", engine1);
    engine1->waitForReadyRead();
    sendToEngine("begin\r\n", engine1);
    //engine1->waitForReadyRead();
    sendToEngine("123456\r\n", engine1);
    //engine1->waitForReadyRead();

    //if((!isAI[1] || engine1) && (!isAI[2] || engine2))
    //    setDiceEnabled(true);

    isAI[1] = true;
}

void MainWindow::loadEngineBClicked()
{
    QString filename = QFileDialog::getOpenFileName(this, "open engine");
    engine2 = openEngine(filename);

    connect(engine2, SIGNAL(readyReadStandardOutput()), this, SLOT(receiveEngineDataB()));

    engine2->start(filename);

    engine2->waitForStarted();

    sendToEngine("name?\r\n", engine2);
    engine2->waitForReadyRead();
    if(first == 1)
        sendToEngine("redfirst\r\n", engine2);
    else if(first == 2)
        sendToEngine("bluefirst\r\n", engine2);
    sendToEngine("blue\r\n", engine2);
    engine2->waitForReadyRead();
    sendToEngine("begin\r\n", engine2);
    //engine1->waitForReadyRead();
    sendToEngine("123456\r\n", engine2);

    //if((!isAI[1] || engine1) && (!isAI[2] || engine2))
    //    setDiceEnabled(true);
    isAI[2] = true;
}

void MainWindow::setEngineRedEnabled(bool flag)
{
    ui->loadEngineR->setEnabled(flag);
    ui->unloadEngineR->setEnabled(flag);
}

void MainWindow::setEngineBlueEnabled(bool flag)
{
    ui->loadEngineB->setEnabled(flag);
    ui->unloadEngineB->setEnabled(flag);
}

void MainWindow::unloadEngineBClicked()
{
    engine2->close();
    engine2 = nullptr;

    //setDiceEnabled(false);
    isAI[2] = false;
}

void MainWindow::unloadEngineRClicked()
{
    engine1->close();
    engine1 = nullptr;

    //setDiceEnabled(false);
    isAI[1] = false;
}

void MainWindow::newGame()
{
    setDiceEnabled(true);
}

int MainWindow::isGameOver()
{
    if(chess[1][1] % 10 == 2)
        return 2;
    if(chess[5][5] % 10 == 1)
        return 1;

    int numR = 0, numB = 0;
    for(int i = 1; i <= 5; i++)
    {
        for(int j = 1; j <= 5; j++)
        {
            if(chess[i][j] % 10 == 1)
                numR++;
            if(chess[i][j] % 10 == 2)
                numB++;
        }
    }

    if(numR == 0)
        return 2;
    if(numB == 0)
        return 1;
    return 0;
}

void MainWindow::doGameOver(int player)
{
    setDiceEnabled(false);

    gameOverDlg * dlg = new gameOverDlg(this);

    dlg->wins(player);

    dlg->show();
}

void MainWindow::openHelpDlg()
{
    HelpDlg * dlg = new HelpDlg(this);

    dlg->show();
}

void MainWindow::openAboutDlg()
{
    AboutDlg * dlg = new AboutDlg(this);

    dlg->show();
}

void MainWindow::clearOutput()
{
    ui->textEdit->clear();
}

void MainWindow::reset()
{
    if(isAI[1])
    {
        isAI[1] = false;
        engine1->close();
        engine1 = nullptr;
        ui->redName->setText("red");
    }

    if(isAI[2])
    {
        isAI[2] = false;
        engine2->close();
        engine2 = nullptr;
        ui->blueName->setText("blue");
    }

    receiveSettings(1, 123456, 123456);

    setDiceEnabled(false);

    for(int i = 1; i <= 5; i++)
        ui->board->clearState(i);
}
